# -*- coding: utf-8 -*-
"""
Module Description: 角色信息缓存及获取，主要用于榜单
Date: 2018/4/16
Author: FQ
"""
import json

from pyshare.dal.repository_impl import RoleRepository, AllianceRepository, CrossRuinsRoleRepository, EmperorRepository
from pyshare.redis_cache.cross.role_cache import get_role_cache_by_type, RoleCacheType, alliance_cache
from pyshare.core.context import temporary_ctx


class BaseCacheInfo(object):
    # 信息缓存基类
    def __init__(self, **kwargs):
        self.field_list = []
        for k, v in self.default_var.items():
            val = kwargs.get(k, v)
            setattr(self, k, val)
            self.field_list.append(k)

    def get_fields(self):
        return self.field_list

    def __getitem__(self, item):
        return getattr(self, self.field_list[item])

    def _asdict(self):
        to_dict = dict()
        for k in self.field_list:
            to_dict[k] = getattr(self, k)
        return to_dict


class RoleInfo(BaseCacheInfo):
    default_var = {'nick_name': "", 'head_id': 1, 'vip_lv': 0, 'alliance_name': "", 'role_guid': "",
                   'role_lv': 1, "small_head_id": 1, "head_frame_id": 1, "privacy_setting": {}, "service_id": 0,
                   'clothes_color': []}


class SimpleRoleInfo(BaseCacheInfo):
    default_var = {'nick_name': "", 'role_guid': ""}


class LeaderBoardRoleInfo(BaseCacheInfo):
    # 跨服国力排行榜
    default_var = {'nick_name': "", 'head_id': 1, 'vip_lv': 0, 'alliance_name': "",
                   'role_guid': "", "small_head_id": 1, "head_frame_id": 1}


# 角色信息缓存列表
RoleInfoList = [RoleInfo, SimpleRoleInfo, LeaderBoardRoleInfo]


class AllianceInfo(BaseCacheInfo):
    # 联盟信息缓存
    default_var = {'id': 0, 'name': ""}


class SelectSql(object):
    RoleAllianceName = "SELECT b.`Name`, a.RoleGuid from " \
                       "TbAllianceMember a join TbAlliance b on a.AllianceId=b.Id " \
                       "where a.RoleGuid in ({l})"


class RoleInformation(object):
    """
    角色信息
    """

    @staticmethod
    def get_many_player_alliance_name(rid_list, **kwargs):
        """
        获取角色所属联盟
        :param rid_list:
        :return:
        """

        alliance_dict = {r['RoleGuid']: r['Name'] for r in AllianceRepository.filter_name_by_rid_list(rid_list)}
        rid_alliance_name_dict = {}
        for rid in rid_list:
            rid_alliance_name_dict[rid] = alliance_dict.get(rid, '')

        return rid_alliance_name_dict

    @classmethod
    def get_role_information_from_db(cls, server_dict, need_alliance=False, info_index=0, **kwargs):
        """
        获取多区服角色信息 昵称/所属联盟
        :param server_dict: {server_id: rid_list}
        :param need_alliance: 是否需要联盟信息
        :param info_index: 角色信息存储RoleInfoList下标
        :return: {rid: RoleInfoList[info_index]}
        """
        role_dict = {}
        role_alliance_name = {}
        for server_id, rid_list in server_dict.iteritems():
            if not rid_list:
                continue

            # 指定访问的区服
            with temporary_ctx(server_id=server_id):
                # 获取所属联盟
                if need_alliance:
                    role_alliance_name = cls.get_many_player_alliance_name(rid_list, **kwargs)

                emperor_role_list = {role.role_guid: role.clothes_color for role in EmperorRepository.filter_by_rid_list(
                        rid_list, 'RoleGuid', 'ClothesColor')}
                # 获取昵称
                role_list = RoleRepository.filter_by_rid_list(rid_list, 'RoleGuid', 'SmallHeadId', 'HeadId',
                                                              'NickName', 'VipLv', 'RoleLv', 'HeadFrameId',
                                                              'PrivacySetting', 'ServiceId')

                fields = RoleInfoList[info_index]().get_fields()
                for role in role_list:
                    role_i = {}
                    for field in fields:
                        if field == 'alliance_name':
                            role_i[field] = role_alliance_name.get(role.role_guid, '')
                        elif field == 'clothes_color':
                            role_i[field] = emperor_role_list.get(role.role_guid, [])
                        else:
                            role_i[field] = getattr(role, field)
                    role_dict[role.role_guid] = RoleInfoList[info_index](**role_i)

        return role_dict

    @classmethod
    def get_role_information_from_cache(cls, rid_list, cache, cache_key, info_index=0):
        """
        从缓存获取角色信息
        :param rid_list: [rid]
        :param cache:
        :param cache_key: 缓存key参数
        :param info_index: 角色信息存储RoleInfoList下标
        :return:
        """
        role_dict = dict()
        not_find = list()
        data_list = cache.get_many_role_cache_data(rid_list, *cache_key)
        for rid, data in zip(rid_list, data_list):
            if data:
                role_cache = json.loads(data)
                role_cache.update({'role_guid': rid})
                role_dict.update({rid: RoleInfoList[info_index](**role_cache)})
            else:
                not_find.append(rid)
        return role_dict, not_find

    @classmethod
    def get_role_information(cls, all_rid_list, server_dict, need_alliance=False, cache_type=RoleCacheType.ActivityHash,
                             cache_key=None, info_index=0, need_db=False, **kwargs):
        """
        先从缓存获取，取不到在从db获取
        :param all_rid_list: 所有角色rid
        :param server_dict: {server_id: [rid]}
        :param need_alliance: 是否需要联盟信息
        :param cache_type: 缓存对象
        :param cache_key:
        :param info_index: 角色信息存储RoleInfoList下标
        :param need_db: 是否直接从数据库中获取数据(防止联盟信息不实时)
        :return: {rid: RoleInfoList[info_index]}
        """
        cache_key = [] if cache_key is None else cache_key
        cache = get_role_cache_by_type(cache_type)
        role_dict, not_find_list = cls.get_role_information_from_cache(all_rid_list, cache, cache_key,
                                                                       info_index=info_index)

        for server_id, rid_list in server_dict.iteritems():
            if not rid_list:
                continue
            if need_db:
                # 缓存中未获取数据的角色，从DB获取
                role_dict.update(cls.get_role_information_from_db({server_id: list(rid_list)},
                                                                  need_alliance=need_alliance,
                                                                  info_index=info_index, **kwargs))
            not_find_rid_set = set(not_find_list) & set(rid_list)
            if not not_find_rid_set:
                continue

            # 缓存中未获取数据的角色，从DB获取
            role_dict.update(cls.get_role_information_from_db({server_id: list(not_find_rid_set)},
                                                              need_alliance=need_alliance,
                                                              info_index=info_index, **kwargs))
        return role_dict

    @classmethod
    def set_role_information(cls, server_dict, need_alliance=False, cache_type=RoleCacheType.ActivityHash,
                             cache_key=None, cycle=-1, del_first=False, info_index=0, **kwargs):
        """
        设置角色信息缓存
        :param server_dict: {server_id: [rid, ....]}
        :param need_alliance:
        :param cache_type:
        :param cache_key:
        :param cycle: 缓存周期
        :param del_first: 更新前是否先清除
        :param info_index: 角色信息存储RoleInfo下标
        :return:
        """
        cache_key = [] if cache_key is None else cache_key
        role_dict = cls.get_role_information_from_db(server_dict, need_alliance=need_alliance,
                                                     info_index=info_index, **kwargs)
        cache = get_role_cache_by_type(cache_type)
        if del_first:
            cache.del_role_cache(*cache_key)
        if role_dict:
            cache.format_and_set_cache_data(role_dict, *cache_key, cycle=cycle)

    @classmethod
    def get_single_role_from_db(cls, rid, server_id):
        """
        获取单个角色
        :param rid:
        :param server_id:
        :return:
        """
        # 指定访问的区服
        with temporary_ctx(server_id=server_id):
            # 获取昵称
            return RoleRepository.get_by_rid(rid, 'RoleGuid', 'SmallHeadId', 'HeadId', 'NickName', 'VipLv', 'RoleLv')


class RuinsRoleInformation(RoleInformation):
    """
    跨服遗迹角色缓存使用
    联盟id 从TbCrossRuinsRole获取 ，不从TbAllianceMember获取
    """

    @staticmethod
    def get_many_player_alliance_name(rid_list, ruins_id=None):
        """
        获取角色所属联盟
        :param rid_list:
        :param ruins_id: 活动id
        :return:
        """
        if not ruins_id:
            raise Exception('func get_many_player_alliance_name param ruins_id is None')
        alliance_id_list = []
        rid_to_alliance_id = {}
        for record in CrossRuinsRoleRepository.filter_by_rid_list(rid_list, ruins_id):
            rid_to_alliance_id[record.role_guid] = record.alliance_id
            if record.alliance_id:
                alliance_id_list.append(record.alliance_id)

        alliance_dict = {a.id: a.name for a in AllianceRepository.filter_by_id_list(alliance_id_list)}
        rid_alliance_name_dict = {}
        for rid, a_id in rid_to_alliance_id.iteritems():
            rid_alliance_name_dict[rid] = alliance_dict.get(a_id, '')

        return rid_alliance_name_dict


class AllianceInformation(object):
    """
    联盟信息
    """

    @classmethod
    def get_alliance_information_from_db(cls, server_dict):
        """
        获取多个区服联盟信息
        :param server_dict: {server_id: alliance_id_list}
        :return:
        """
        alliance_info_dict = {}
        for server_id, alliance_id_list in server_dict.iteritems():
            if not alliance_id_list:
                continue

            # 指定访问的区服
            with temporary_ctx(server_id=server_id):
                for alliance in AllianceRepository.filter_by_id_list(alliance_id_list, 'Id', 'Name'):
                    alliance_info_dict[alliance.id] = alliance.name
                    a = {'id': alliance.id, 'name': alliance.name}
                    alliance_info_dict.update({alliance.id: AllianceInfo(**a)})

        return alliance_info_dict

    @classmethod
    def get_alliance_information_from_cache(cls, alliance_id_list, cache_key):
        """
        从缓存获取联盟信息
        :param alliance_id_list:
        :param cache_key: 缓存key拼接参数（一般是活动id）
        :return:
        """
        alliance_dict = {}
        not_find = []
        data_list = alliance_cache.get_many_alliance_cache_data(alliance_id_list, *cache_key)
        for alliance_id, data in zip(alliance_id_list, data_list):
            if data:
                a = json.loads(data)
                a.update({'id': alliance_id})
                alliance_dict.update({alliance_id: AllianceInfo(**a)})
            else:
                not_find.append(alliance_id)

        return alliance_dict, not_find

    @classmethod
    def get_alliance_information(cls, all_alliance_id_list, server_dict, cache_key=None):
        """
        先从缓存获取，取不到在从db获取
        :param all_alliance_id_list: 所有联盟id
        :param server_dict: {server_id: [alliance_id]}
        :param cache_key:
        :return: {rid: AllianceInfo}
        """

        cache_key = [] if cache_key is None else cache_key
        alliance_dict, not_find_list = cls.get_alliance_information_from_cache(all_alliance_id_list, cache_key)

        for server_id, alliance_id_list in server_dict.iteritems():
            if not alliance_id_list:
                continue
            not_find_set = set(not_find_list) & set(alliance_id_list)
            if not not_find_set:
                continue

            # 缓存中未获取数据的角色，从DB获取
            alliance_dict.update(cls.get_alliance_information_from_db({server_id: list(not_find_set)}))
        return alliance_dict

    @classmethod
    def set_alliance_information(cls, server_dict, cache_key=None, cycle=-1, del_first=False):
        """
        设置角色信息缓存
        :param server_dict: {alliance_id: AllianceInfo}
        :param cache_key: 缓存key填充值 一般是活动id
        :param cycle: 缓存周期
        :param del_first: 更新前是否先清除
        :return:
        """
        role_dict = cls.get_alliance_information_from_db(server_dict)
        if cache_key is None:
            cache_key = []
        if del_first:
            alliance_cache.del_role_cache(*cache_key)

        alliance_cache.format_and_set_cache_data(role_dict, *cache_key, cycle=cycle)
